

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using TrackSystem.Entities.Extensions;
using TrackSystem.IO.Alarm;
using TrackSystem.IO.Models;

namespace TrackSystem.IO.Protocol
{
    /// <summary>
    /// SDI视频字符叠加协议（网口）
    /// 0- 手动登录
    /// 1- 初始化显示时间 
    /// 2- 显示透明度
    /// 3- 显示背景颜色  
    /// 4- 显示字体类型  0-宋体 1-楷体
    /// 5- 显示字体大小  默认36(16进制24)
    /// 6- 显示字体颜色
    /// 7- 航向heading
    /// 8- 深度depth 
    /// 9- 高度altitude 
    /// 10-经度Lon
    /// 11-纬度Lat
    /// 12-潜次编号1
    /// 13-潜次编号2
    /// 20-清空字符串信息
    /// </summary>
    public class SDIOSDProtocol : ProtocolBase
    {
        private int startTicks;
        private int repeatTimes;    //写入操作重复次数

        private readonly int roLength;
        private readonly int rwLength;

        private byte[] Tx_hp, Tx_login, Tx_clear, Tx_based,Tx_time_init;       //心跳包，登录,清空，基础命令
        private byte[] Tx_OSD0, Tx_OSD1, Tx_OSD2, Tx_OSD3;  //时间显示、head-depth-altimeter,lon-lat, 潜次
        private bool bSendR, bSendW;     // 发送标志
        private int OSDLength = 512;  // 添加字符的总长度为512，不足部分补 0x00
        private string time_Temp; // 时间
        private String head = "000.0";
        private String depth = "000.0";
        private String altitude = "00.0";
        private String lon = "000.000000";
        private String lat = "00.000000";
        private String dive1 = "#";
        private String dive2 = "#";
        private string OSD1, OSD2, OSD3;
        private int lastime_send = 0;
        private int count_init_time = 0;


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="name"></param>
        public SDIOSDProtocol(string name,int roLength,int rwLength)
            : base()
        {
            Name = name;
            this.roLength = roLength;
            this.rwLength = rwLength;
            Tx_hp = new byte[8] { 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x00, 0x00 };
            Tx_clear = new byte[28] { 0x41, 0x41, 0x41, 0x41, 0x14, 0x00, 0x00, 0x00, 0x02, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 
                0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF };
            Tx_login = new byte[144] { 0x41, 0x41, 0x41, 0x41, 0x88, 0x00, 0x00, 0x00, 0x01, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 
                0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x64, 0x6D, 0x69, 0x6E, 
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0xFF, 0x9C, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00};
            Tx_based = new byte[72] {0x41, 0x41, 0x41, 0x41, 0x40, 0x02, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 
                0x00, 0x00, 0x00, 0x30, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00};
            Tx_OSD0 = new byte[72] { 0x41, 0x41, 0x41, 0x41, 0x40, 0x00, 0x00, 0x00, 0x03, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 
                0x00, 0x00, 0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC1, 0xBC, 0xFF, 0x00, 0x00, 
                0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00};
            Tx_OSD1 = new byte[72] {0x41, 0x41, 0x41, 0x41, 0x40, 0x02, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,
                0x00, 0x00, 0x00, 0x30, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0xDF, 0x02, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00};
            Tx_OSD2 = new byte[72] {0x41, 0x41, 0x41, 0x41, 0x40, 0x02, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,
                0x00, 0x00, 0x00, 0x30, 0x02, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x06, 0x02, 0x00, 0x00, 0xF6, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00};
            Tx_OSD3 = new byte[72] {0x41, 0x41, 0x41, 0x41, 0x40, 0x02, 0x00, 0x00, 0x01, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A,
                0x00, 0x00, 0x00, 0x30, 0x02, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0xF6, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00,
                0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00};
            bSendR = false;
            bSendW = false;
            NeedInit = true;
        }

        /// <summary>
        /// 生成读取寄存器的通讯帧
        /// </summary>
        /// <returns></returns>
        public override byte[] GenerateReadFrame(out int NeedRecvLen)
        {
            startTicks = Environment.TickCount;
            
            if (bSendR)
            {
                Fail_R++;
                // 失败超过3次，成功次数清零
                if (Fail_R >= 3)
                {
                    Succ_R = 0;
                }
            }
            bSendR = true;
            if (NeedInit) {
                NeedInit = false;
                NeedRecvLen = 8;
                return getTx_login();
            }
            else
            {
                bSendR = false;
                NeedRecvLen = 0;
                return null;
            }
            //if (startTicks - lastime_send >= 7000) // 大于7s,发送心跳包
            //{
            //    NeedRecvLen = 8;
            //    lastime_send = startTicks;
            //    return Tx_hp;
            //}
            //else {
            //    bSendR = false;
            //    NeedRecvLen = 0;
            //    return null;
            //}
        }


        /// <summary>
        /// 根据收到的内容进行处理
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public override bool ProcessReadFrame(byte[] buffer)
        {
            Rx_r = buffer;
            if (Rx_r != null)
            {
                Succ_R++;
                Fail_R = 0;
                bSendR = false;
                Lapse = Environment.TickCount - startTicks;
                return true;
            }
            else {
                Lapse = Environment.TickCount - startTicks;
                return false;
            }
        }


        /// <summary>
        /// 生成写入寄存器的通讯帧
        /// </summary>
        /// <param name="NeedRecvLen"></param>
        /// <returns></returns>
        public override byte[] GenerateWriteFrame(out int NeedRecvLen)
        {
            startTicks = Environment.TickCount;
            // 上次没有收到正确的返回帧
            if (bSendW)
            {
                Fail_W++;
                Succ_W = 0;
            }
            // 待写入的数据队列非空 
            if (writeQueue.Count() > 0)
            {
                repeatTimes = 3;    // 写入的重复次数更新为3次
            }

            // 生成写入数据帧

            if (WriteFlag && repeatTimes-- > 0)
            {
                while (writeQueue.TryDequeue(out AddressAndVal data))
                {
                    if (data.Address == 0 && data.Value == 1)
                    { // 手动登录
                        NeedInit = true;
                        count_init_time = 0;
                    }
                    else if (data.Address == 1 && data.Value == 1 && count_init_time < 10) // 初始化显示时间
                    {
                        count_init_time++;
                        bSendW = true;
                        NeedRecvLen = 0;
                        return Tx_OSD0;
                    }
                    else if (data.Address == 7)
                    {
                        head = Convert.ToString(Math.Round(data.Value, 1));
                    }
                    else if (data.Address == 8)
                    {
                        depth = Convert.ToString(Math.Round(data.Value, 1));
                    }
                    else if (data.Address == 9)
                    {
                        altitude = Convert.ToString(Math.Round(data.Value, 1));
                        OSD1 = "head: " + head + "   " + "D: " + depth + "   " + "A: " + altitude;
                        //bSendW = true;
                        //byte[] OSD1Byte = Encoding.Default.GetBytes(OSD1);
                        //byte[] Tx_OSD1_temp = Tx_OSD1.Concat(OSD1Byte).ToArray();
                        //int OSD_zero = OSDLength - Tx_OSD1_temp.Length;
                        //byte[] Tx_time_add = new byte[OSD_zero];
                        //Tx_OSD1_temp = Tx_OSD1_temp.Concat(Tx_time_add).ToArray();
                        //NeedRecvLen = 0;
                        //return Tx_OSD1_temp;
                    }
                    else if (data.Address == 10)
                    {
                        if (data.Value >= 0)
                        {
                            lon = Convert.ToString(data.Value) + "°E";   // lat
                        }
                        else
                        {
                            lon = Convert.ToString(data.Value) + "°W";   // lat
                        }
                    }
                    else if (data.Address == 11)
                    {
                        if (data.Value >= 0)
                        {
                            lat = Convert.ToString(data.Value) + "°N";   // lat
                        }
                        else
                        {
                            lat = Convert.ToString(data.Value) + "°S";   // lat
                        }
                        OSD2 = "Lon: " + lon + "   " + "Lat: " + lat + "      " + OSD1;
                        bSendW = true;
                        byte[] OSD2Byte = Encoding.Default.GetBytes(OSD2);
                        byte[] Tx_OSD2_temp = Tx_OSD2.Concat(OSD2Byte).ToArray();
                        int OSD_zero = OSDLength - Tx_OSD2_temp.Length;
                        byte[] Tx_time_add = new byte[OSD_zero];
                        Tx_OSD2_temp = Tx_OSD2_temp.Concat(Tx_time_add).ToArray();
                        NeedRecvLen = 0;
                        return Tx_OSD2_temp;
                    }
                    else if (data.Address == 12)
                    {
                        dive1 = Convert.ToString(data.Value);
                    }
                    else if (data.Address == 13)
                    {
                        dive2 = Convert.ToString(data.Value);
                        OSD3 = "Dive: " + dive1 + "-" + dive2;
                        bSendW = true;
                        byte[] OSD3Byte = Encoding.Default.GetBytes(OSD3);
                        byte[] Tx_OSD3_temp = Tx_OSD3.Concat(OSD3Byte).ToArray();
                        int OSD_zero = OSDLength - Tx_OSD3_temp.Length;
                        byte[] Tx_time_add = new byte[OSD_zero];
                        Tx_OSD3_temp = Tx_OSD3_temp.Concat(Tx_time_add).ToArray();
                        NeedRecvLen = 0;
                        return Tx_OSD3_temp;
                    }
                    else if (data.Address == 20 && data.Value == 1) {
                        bSendW = true;
                        NeedRecvLen = 0;
                        return Tx_clear;
                    }
                }
                NeedRecvLen = 0;
                return null;
            }

            // 如果写入次数超过三次未成功，则将队列清空
            if (repeatTimes <= 0)
            {
                AlarmFactory.Instance.Insert("Write data error, more than three attempts",
                    AlarmCategory.COMM, Name, 0);

                // 标志位复位 重启下一个批次的读写任务
                WriteFlag = false;
            }
            NeedRecvLen = 0;
            return null;
        }

        /// <summary>
        /// 根据收到的内容进行处理
        /// </summary>
        /// <param name="buffer"></param>
        /// <returns></returns>
        public override bool ProcessWriteFrame(byte[] buffer)
        {
            Lapse = Environment.TickCount - startTicks;
            int nLen = buffer.Length;
            Rx_w = buffer;
            Succ_W++;
            bSendW = false;
            Fail_W = 0;  // 通信成功，则将失败次数清零
            // WriteFlag = false;
            repeatTimes = 0;
            return true;
        }
        public override void PushRegValue(int Index, double Value)
        {
            Index -= roLength;  //寄存器存储顺序从0开始，先是只读存储器，后面是读写存储器

            if (Index >= 0 && (Index < rwLength)&&!NeedInit)
            {
                if (Index == 4) {
                    int value_temp = (int)Value;
                    Tx_based[56] = (byte)(value_temp & 0xFF);
                    Tx_OSD0[56] = (byte)(value_temp & 0xFF);
                    Tx_OSD1[56] = (byte)(value_temp & 0xFF);
                    Tx_OSD2[56] = (byte)(value_temp & 0xFF);
                    Tx_OSD3[56] = (byte)(value_temp & 0xFF);
                }
                else if (Index == 5)
                {
                    int value_temp = (int)Value;
                    Tx_based[60] = (byte)(value_temp & 0xFF);
                    Tx_OSD0[60] = (byte)(value_temp & 0xFF);
                    Tx_OSD1[60] = (byte)(value_temp & 0xFF);
                    Tx_OSD2[60] = (byte)(value_temp & 0xFF);
                    Tx_OSD3[60] = (byte)(value_temp & 0xFF);
                }
                else
                {
                    writeQueue.Enqueue(new AddressAndVal(Index, Value));
                    WriteFlag = true;
                }
            }
        }

        public byte[] getTx_login() {
            //ToUniversalTime()转换为标准时区的时间,去掉的话直接就用北京时间
            TimeSpan ts = DateTime.Now.ToUniversalTime() - new DateTime(1970,1,1, 0, 0, 0, 0,DateTimeKind.Utc);
            //得到精确到秒的时间戳（长度10位）
            String time =  Convert.ToInt64(ts.TotalSeconds).ToString("X2");//(1637887870).ToString("X2"); //
            System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1));//当地时区
            var timeTemp = startTime.AddSeconds(Convert.ToInt64(ts.TotalSeconds));
            byte[] timeByte = strToHexByte(time);
            Tx_login[104] = timeByte[3];
            Tx_login[105] = timeByte[2];
            Tx_login[106] = timeByte[1];
            Tx_login[107] = timeByte[0];
            return Tx_login;
        }


        public static byte[] strToHexByte(string hexString)
        {
            hexString = hexString.Replace(":", "");
            if ((hexString.Length % 2) != 0)
                hexString += " ";
            byte[] returnBytes = new byte[hexString.Length / 2];
            for (int i = 0; i < returnBytes.Length; i++)
                returnBytes[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
            return returnBytes;
        }
    }
}

